package com.example.my_helper;

import android.content.Context;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Queue;
import android.os.Handler;
import android.util.Log;

import cn.wch.ch34xuartdriver.CH34xUARTDriver;

public class MyUARTDriver {

    final static int ON_CONNECT = 0;
    final static int ON_MESSAGE = 1;
    final static int ON_SEND = 2;
    final static int ON_CLOSE = 3;
    final static int ON_ERROR = 4;
    final static int NOT_FOUND = 5;
    final static int NO_PERMISSION = 6;
    final static int CONFIG_ERROR = 7;
    final static int UNSUPPORT = 8;

    private static final String ACTION_USB_PERMISSION = "cn.wch.wchusbdriver.USB_PERMISSION";
    public boolean isOpen = false;
    public int baudRate = 115200;
    public byte dataBit = 8;
    public byte stopBit = 0;
    public byte parity = 0;
    public byte flowControl = 0;

    public int buf_len = 0;
    public int recv_len = 0;
    public byte[] buf = new byte[4096];
    public CH34xUARTDriver device;
    private int recv_tick = 0;
    Queue<Byte> recv_queue;
    public Context context;
    public Handler handler;
    private UsbManager usbManager;
    private UsbDevice usbDevice;

    public boolean use_console_log = true;
    SerialDebuger serialDebuger;
    RingBuffer ringBuffer;

    public MyUARTDriver(Context context, Handler handler)
    {
        this.context = context;
        this.handler = handler;
        usbManager =  (UsbManager) context.getSystemService(Context.USB_SERVICE);
        this.device = new CH34xUARTDriver(usbManager, context, ACTION_USB_PERMISSION);
        this.ringBuffer = new RingBuffer(1024*10);
        //this.recv_queue = new LinkedList<>();
    }

    public boolean UsbSupported()
    {
        return device.UsbFeatureSupported();
    }

    public int RequestPermission()
    {
        usbDevice = device.EnumerateDevice();
        if(usbDevice == null){
            handler.sendEmptyMessage(NOT_FOUND);
            return NOT_FOUND;
        }
        if(!usbManager.hasPermission(usbDevice)) {
            device.OpenDevice(usbDevice);
            int res = 60;
            while (!usbManager.hasPermission(usbDevice)) {
                res--;
                if (res == 0) {
                    handler.sendEmptyMessage(NO_PERMISSION);
                    return NO_PERMISSION;
                }
                try {
                    Thread.sleep(100);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            try {
                Thread.sleep(100);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        try {
            device.ResumeUsbList();
            if (!device.isConnected()) {
                device.ResumeUsbList();
            }
            if (!device.isConnected()) {
                device.ResumeUsbList();
            }
            Thread.sleep(100);
            device.CloseDevice();
            Thread.sleep(100);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0;
    }

    public int Open()
    {
        if (!isOpen) {
            if(!UsbSupported())
            {
                return UNSUPPORT;
            }
            int retval;
            retval = RequestPermission();
            if(retval != 0)
                return retval;
            retval = device.ResumeUsbList();
            if(retval == 0) {
                if (!device.UartInit()) {//对串口设备进行初始化操作
                    device.CloseDevice();
                    if(use_console_log)
                        handler.sendEmptyMessage(ON_ERROR);
                    return ON_ERROR;
                }
                if (!device.SetConfig(baudRate, dataBit, stopBit, parity, flowControl)) //配置串口波特率，函数说明可参照编程手册
                {
                    device.CloseDevice();
                    if(use_console_log)
                        handler.sendEmptyMessage(CONFIG_ERROR);
                    return CONFIG_ERROR;
                }
                isOpen = true;
                new Thread(new recv_thread()).start();
            }
        }
        if(use_console_log)
            handler.sendEmptyMessage(ON_CONNECT);
        return  ON_CONNECT;
    }

    public int Write(byte[] bytes, int len)
    {
        return device.WriteData(bytes, len);
    }

    public int Write(byte[] bytes, int offset, int len)
    {
        return device.WriteData(bytes, offset, len);
    }

    public int Write(String string)
    {
        return device.WriteData(string.getBytes(), string.length());
    }

    public Boolean WriteByte(byte b)
    {
        byte[] bytes = new byte[1];
        bytes[0] = b;
        int len = device.WriteData(bytes, 1);

        return len > 0;
    }

    public int WriteLine(String string)
    {
        return Write(string+"\r\n");
    }

    public void Close()
    {
        if(isOpen)
        {
            isOpen = false;
            try{
                device.CloseDevice();
            }catch (Exception e){
                e.printStackTrace();
            }
            //if(use_console_log)
            handler.sendEmptyMessage(ON_CLOSE);
        }
    }

    class recv_thread extends Thread {
        public void run() {
            byte[] tmp = new byte[1024];
            int len;
            boolean flag = false;
            boolean res;
            device.ReadData(tmp, 1024); //清理缓存
            while (isOpen) {
                try {
                    len = device.ReadData(tmp, 1024);
                    if(len > 0)
                    {
                        recv_tick = 0;
                        if(use_console_log) {
                            System.arraycopy(tmp, 0, buf, buf_len, len);
                            buf_len += len;
                            if (buf_len >= 1024) {
                                handler.removeMessages(ON_MESSAGE);
                                serialDebuger.Log_recv(buf, buf_len);
                                buf_len = 0;
                            }
                        }else{
                            ringBuffer.write(tmp, 0, len, 500);
                        }
                        flag = true;
                    }
                    else if(flag && use_console_log)
                    {
                        recv_tick++;
                        if(recv_tick > 20){
                            handler.removeMessages(ON_MESSAGE);
                            serialDebuger.Log_recv(buf, buf_len);
                            buf_len = 0;
                            flag = false;
                        }
                    }
                    Thread.sleep(1);
                    if(!device.isConnected()){
                        Close();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    serialDebuger.Log_i(e.toString());
                    Close();
                }
            }

        }
    }


    InputStream inputStream = new InputStream() {
        @Override
        public int read() throws IOException {
            try {
                return ringBuffer.get(-1);
            }catch (Exception e){
                e.printStackTrace();
                throw new IOException(e.getMessage());
            }
        }

        @Override
        public int read(byte[] b) throws IOException {
            return ringBuffer.read(b, 0, b.length);
        }

        @Override
        public int read(byte[] b, int off, int len) throws IOException {
            return ringBuffer.read(b, off, len);
        }

        @Override
        public int available() throws IOException {
            return ringBuffer.getSize();
        }
    };

    OutputStream outputStream = new OutputStream() {
        @Override
        public void write(int b) throws IOException {
            MyApp.myUARTDriver.WriteByte((byte) b);
        }

        @Override
        public void write(byte[] b) throws IOException {
            MyApp.myUARTDriver.Write(b, b.length);
        }

        @Override
        public void write(byte[] b, int off, int len) throws IOException {
            MyApp.myUARTDriver.Write(b, off, len);
        }

    };

    public InputStream getInputStream(){
        return inputStream;
    }

    public OutputStream getOutputStream(){
        return outputStream;
    }
}
